/*
 * Comparisor.cpp
 *
 *  Created on: Aug 14, 2013
 *      Author: jeff
 */

#include "Comparisor.h"

#include <memory.h>
#include <hash/md5.h>

Comparisor::Comparisor():
	compare_from_left(0),
	comare_from_right(0),
	offset_left(0),
	offset_right(0),
	chunk_size(CHUNK_SIZE){}

void Comparisor::diff(const char* left, int left_size, const char* right, int right_size, Json::Value* result)
{
	int delete_length;
	int insert_length;
	char* insert_buffer;

	int size = left_size < right_size?left_size:right_size;
	if(0 < size)
	{
		int offset = find_first_diff(left, right, size, &delete_length, &insert_length, &insert_buffer);

		int left_offset = (size==(int)offset)?(size):(offset + (int)insert_length);
		int right_offset = (size==(int)offset)?(size):(offset + (int)delete_length);

		save_diff_info(offset, delete_length, insert_length, insert_buffer, result);

		left_size = left_size - (left_offset);
		right_size = right_size - (right_offset);
		diff(left+left_offset, left_size, right+right_offset, right_size, result);
	}
	else if(left_size != right_size)
	{
		if(0 < left_size)
		{
			save_diff_info(size, 0, left_size, left+size, result);
		}
		else if(0 < right_size)
		{
			save_diff_info(size, right_size, 0, NULL, result);
		}
	}
}

/**
 * Compare buffers, if they are different, return the first mismatch point, direction and length.
 */
int Comparisor::find_first_diff(const char* left, const char* right, const long long size, int *delete_length, int *insert_length, char **insert_buffer)
{
	int offset = 0;
	(*insert_length) = 0;
	(*delete_length) = 0;

	bool diff = false;
	for(int i = 0; i< size; i++)
	{
		if(left[i] != right[i])
		{
			diff = true;
			int block_length = chunk_size;
			for(int j=0; j<size-offset; j++)
			{
				bool match = false;
				if(offset + (*insert_length)>= size)
					break;

				(*delete_length) = 0;
				while(true){
					// Check remain length of right
					//block_length = (diff_position + delete_length + chunk_size)>=size?size-(diff_position + delete_length):chunk_size;
					// Check remain length of left
					//block_length = block_length>=(size-diff_position-j)?(size-diff_position-j):block_length;
					//if(0 == block_length)
					//{
					//	break;
					//}

					if(block_length > size- offset - (*delete_length))
						break;
					if(block_length > size-offset-j)
						break;

					match = (0==memcmp(left+offset+j, right+offset+(*delete_length), block_length));

					if(match)
						break;
					(*delete_length)++;
				}

				if(match)
					break;

				(*insert_length)++;
			}
		}

		if(diff)
			break;

		offset = i+1;
	}

	*insert_buffer = (char*)(left+offset);

	return offset;
}

void Comparisor::save_diff_info(int offset, int delete_length, int insert_length, const char * insert_buffer, Json::Value* info)
{
	Json::Value segment;
	segment[JSON_KEY_OFFSET] = offset;
	segment[JSON_KEY_INSERT_LENGTH] = insert_length;
	segment[JSON_KEY_DELETE_LENGTH] = delete_length;

	if(insert_length > 0 && NULL != insert_buffer)
	{
		output_diff_data(insert_length, insert_buffer, &segment);
	}

	(*info)[JSON_KEY_SEGMENT].append(segment);
}

void Comparisor::output_diff_data(int insert_length, const char * diff, Json::Value* segment)
{
	std::string outBuffer;
	outBuffer.append(diff, insert_length);
	(*segment)[JSON_KEY_OUTPUT] = outBuffer;
}


